﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SpotsHub.RouteService.Data;

namespace SpotsHub.RouteService
{
    public class ShipmentManager
    {
        public static Shipment WrapShipment(
            IRouteRepository db,
            Address pickupAddress,
            Address deliveryAddress,
            Contactor pickupContactor,
            Contactor deliveryContactor,
            Submitter submitter)
        {
            // TODO: Shall we set the status here?
            if (db.Addresses.Where(a => a.Id == pickupAddress.Id || a.Id == deliveryAddress.Id).Count() != 0)
            {
                throw new ArgumentException("pickupAddress or deliveryAddress already existed in the database.");
            }

            pickupAddress = db.Addresses.Add(pickupAddress);
            deliveryAddress = db.Addresses.Add(deliveryAddress);
            pickupContactor = db.Contactors.Add(pickupContactor);
            deliveryContactor = db.Contactors.Add(deliveryContactor);
            submitter = db.DBContext.Set<Submitter>().Add(submitter);

            db.SaveChanges();

            Location pickupLocation = db.Locations.Add(new Location()
            {
                Address = pickupAddress,
                Status = LocationStatus.ReadyForPickUp,
                Contactor = pickupContactor
            });

            Location deliveryLocation = db.Locations.Add(new Location()
            {
                Address = deliveryAddress,
                Status = LocationStatus.PendingDropOff,
                Contactor = deliveryContactor
            });

            var s = db.Shipments.Add(new Shipment()
            {
                PickUpLocation = pickupLocation,
                DeliveryLocation = deliveryLocation,
                ConfirmTime = DateTime.UtcNow,
                Submitter = submitter,
                Status = ShipmentStatus.Pending,
                TrackingNumber = db.GetNewTrackingNumber()
            });

            TimeUtility.UpdateTimeStamp(s);

            db.SaveChanges();

            pickupLocation.Shipment = s;
            deliveryLocation.Shipment = s;

            var pkg = new Package() { Shipment = s, Status = PackageStatus.ReadyForPickUp };
            TimeUtility.UpdateTimeStamp(pkg);
            db.Packages.Add(pkg);

            db.SaveChanges();

            return s;
        }

        public static Address GenerateAddress(IRouteRepository db, Nop.Core.Domain.Common.Address externalAddress)
        {
            try 
	        {
                return new Address()
                {
                    Address1 = externalAddress.Address1,
                    Address2 = externalAddress.Address2,
                    City = db.Localities.Single(c => c.Name == externalAddress.City),
                    ZipPostalCode = externalAddress.ZipPostalCode,
                    StateProvince = db.StateProvinces.Single(s => s.Name == externalAddress.StateProvince.Abbreviation),
                    Country = db.Countries.Single(c => c.Name == externalAddress.Country.Name)
                };
	        }
	        catch (InvalidOperationException e)
	        {
		        throw new InvalidOperationException(String.Format("The external address is invalid. Address: {0}", externalAddress), e);
	        }
        }

        public static void FinishShipment(IRouteRepository db, Shipment shipment)
        {
            var s = ValidateShipment(db, shipment);

            if (shipment.PickUpLocation.Status == LocationStatus.Complete && shipment.DeliveryLocation.Status == LocationStatus.Complete)
            {
                shipment.Package.Status = PackageStatus.Delivered;
                shipment.Status = ShipmentStatus.Complete;
            }
        }

        private static Shipment ValidateShipment(IRouteRepository db, Shipment shipment)
        {
            Shipment s = db.Shipments.Find(shipment.Id);
            if (null == s)
            {
                throw new ArgumentException("The shipment information cannot be found.", "shipment");
            }
            if (null == s.Package)
            {
                throw new ArgumentException("The package of the shipment cannot be found.", "shipment");
            }
            return s;
        }

    }

    public class MapManager
    {
        public class MapViewBag
        {
            public string Key { get; set; }
            public string RequestString { get; set; }
        }

        public static MapViewBag RoutePlanToMapView(RoutePlan route)
        {
            // TODO: Verify all parameters
            var wps = new List<string>();
            wps.Add(route.Warehouse.Address.FormattedAddress);
            wps.AddRange(route.AssignedLocations.Select(wp => wp.Address.FormattedAddress));
            wps.Add(route.Warehouse.Address.FormattedAddress);
            string request = Bing.MapRouteHelper.BuildDirectionUrlWithoutKey(wps.ToArray());

            return new MapViewBag() { Key = Bing.MapRouteHelper.BingKey, RequestString = request };
        }
    }
}
